/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package datasource;

import datasource.bmp.BitmapFileHeader;
import datasource.bmp.BitmapV5InfoHeader;
import datasource.bmp.DIBHeader;
import datasource.bmp.ImagenBMP;
import datasource.pixel.Pixel;
import datasource.pixel.RGB;
import datasource.util.BitWise;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import javax.swing.JOptionPane;

/**
 *
 * @author Fabian
 */
public class ImageReader {

    FileInputStream fileInput;
    BufferedInputStream bufferedinput;
    Imagen imagen = null;
    DIBHeader dibheader = null;
    BitmapFileHeader bmfheader = null;
    Pixel[] pixelarray;
    //buffer temporal de lectura
    byte[] buffer;

    //solo carga un bmp (por ahora) y va cargando parte por parte (Bitmapfileheader,dibheader,...etc)
    //para no cargar toda la
    //BitmapFileHeader header, BitmapV5InfoHeader bmiheader, BitmapCoreHeader bmcheader, ColorTable ctable, Pixel[] pixelarray
    public ImageReader(String path) throws IOException {

        try {
            //solo se usa para conocer e tamaño del archivo (NOTA: se supone que si el valor
            //escrito en el offset 2 de bitmapfileheader de tamaño 4 bytes es correcto,
            //esta linea no es necesaria)
            fileInput = new FileInputStream(path);
        } catch (FileNotFoundException ex) {
            JOptionPane.showMessageDialog(null,
                    "No se encontro la imagen: " + path,
                    "Error abriendo la imagen",
                    JOptionPane.OK_OPTION);
        }
        int filesize = (int) (new File(path)).length();
        //System.out.println("Tamaño de archivo: " + filesize + " bytes");
        bufferedinput = new BufferedInputStream(fileInput);
        //carga los dos primeros bytes (cabecera "BM")
        buffer = new byte[2];
        bufferedinput.read(buffer);
        //completar implementacion
        if (buffer[0] == 'B' && buffer[1] == 'M') {
            createBitmapFileHeader(path);
            createDIBHeader(path);
            createPixelArray(dibheader);
            this.imagen = new ImagenBMP(bmfheader, dibheader, null, pixelarray);
        }
        /*
        System.out.println("Red:" + imagen.getPixelarray()[0].getRed());
        System.out.println("Green:" + imagen.getPixelarray()[0].getGreen());
        System.out.println("Blue:" + imagen.getPixelarray()[0].getBlue());
        System.out.println("Red:" + imagen.getPixelarray()[2].getRed());
        System.out.println("Green:" + imagen.getPixelarray()[2].getGreen());
        System.out.println("Blue:" + imagen.getPixelarray()[2].getBlue());
         */
        buffer = null;
        bufferedinput.close();
        fileInput.close();
    }

    public Imagen getImagenCopy() {
        ImagenBMP retorno = (ImagenBMP) this.imagen;
        return retorno.getClone();
    }

    private void createBitmapFileHeader(String path) throws IOException {

        int filesize = 0;
        //variables temporales auxiliares
        long longtemp = 0;
        int inttemp = 0;
        int readed;

        bmfheader = new BitmapFileHeader();


        //compara SI NO SON IGUALES los dos campos de HEADER_FIELD en BitmapFileHeader contra buffer
        if (!((BitmapFileHeader.HEADER_FIELD[0] == buffer[0])
                && (BitmapFileHeader.HEADER_FIELD[1] == buffer[1]))) {
            JOptionPane.showMessageDialog(null,
                    "Ocurrio un error abriendo la imagen" + path
                    + "\n La imagen no cuenta con la cabecera \"BM\"",
                    "Error de formato",
                    JOptionPane.OK_OPTION);
        }
        //lectura e impresion de tamaño de archivo
        buffer = new byte[4];
        readed = bufferedinput.read(buffer);
        longtemp = BitWise.byteArrayToLongLE(buffer);
        bmfheader.setFilesize(longtemp);
        //System.out.println("tamaño de archivo (desde fichero): " + longtemp + " bytes");

        //lectura, impresion y creacion de campos reservados
        buffer = new byte[2];
        readed = bufferedinput.read(buffer);
        inttemp = BitWise.byteArrayToIntLE(buffer);
        //System.out.println("Campo reservado 1: " + inttemp);
        bmfheader.setCreator1(inttemp);
        readed = bufferedinput.read(buffer);
        inttemp = BitWise.byteArrayToIntLE(buffer);
        //System.out.println("Campo reservado 2: " + inttemp);
        bmfheader.setCreator2(inttemp);

        //lectura, impresion y creacion del offset del pixelarray
        buffer = new byte[4];
        readed = bufferedinput.read(buffer);
        longtemp = BitWise.byteArrayToLongLE(buffer);
        //System.out.println("Offset de pixelarray: " + longtemp + " (posicion en bytes)");
        bmfheader.setBmp_offset(longtemp);
        //System.out.println("\n-------------------------------\n");
    }

    private void createDIBHeader(String path) throws IOException {

        //lectura, creacion e impresion del numero de bytes que compone est bloque
        //NOTA: dependiendo del tamaño de este bloque, el procesamiento sobre la imagen varia
        //(por implementar)
        buffer = new byte[4];
        int readed = bufferedinput.read(buffer);
        long longtemp = BitWise.byteArrayToLongLE(buffer);
        int inttemp;
        //System.out.println("tamaño del bloque (DIBHeader): " + longtemp + " bytes");
        //Deacuerd al valor del tamaño del bloque (DIBHeader), decimos que tipo de
        //header es (por ahora solo implementado BitmapV5InfoHeader )
        if (longtemp == 40) {
            //entonces es BitmapV5InfoHeader
            dibheader = new BitmapV5InfoHeader();
            //se leen y crean los campos ancho y alto de la imagen
            buffer = new byte[4];
            readed = bufferedinput.read(buffer);
            longtemp = BitWise.byteArrayToLongLE(buffer);
            dibheader.setWidth(longtemp);
            //System.out.println("ancho: " + longtemp + " bytes");
            buffer = new byte[4];
            readed = bufferedinput.read(buffer);
            longtemp = BitWise.byteArrayToLongLE(buffer);
            dibheader.setHeight(longtemp);
            //System.out.println("alto: " + longtemp + " bytes");

            //se leen y crean los campos numero de planos y numero de bits por pixel
            buffer = new byte[2];
            readed = bufferedinput.read(buffer);
            inttemp = BitWise.byteArrayToIntLE(buffer);
            dibheader.setNplanes(inttemp);
            //System.out.println("numero de planos: " + inttemp);
            buffer = new byte[2];
            readed = bufferedinput.read(buffer);
            inttemp = BitWise.byteArrayToIntLE(buffer);
            dibheader.setBitspp(inttemp);
            //System.out.println("numero de bits por pixel: " + inttemp + " bits");

            //se lee y crea el campo metodo de compresion
            buffer = new byte[4];
            readed = bufferedinput.read(buffer);
            longtemp = BitWise.byteArrayToLongLE(buffer);
            dibheader.setCompress_type(longtemp);
            //System.out.println("Metodo de compresion: " + longtemp);

            //se lee y crea el campo bites size (este campo inluye el "acolchado")
            buffer = new byte[4];
            readed = bufferedinput.read(buffer);
            longtemp = BitWise.byteArrayToLongLE(buffer);
            dibheader.setBmp_bytesz(longtemp);
            System.out.println("bites size+padding: " + dibheader.getBmp_bytesz());

            //se lee y crea el campo resolucion horizontal
            buffer = new byte[4];
            readed = bufferedinput.read(buffer);
            longtemp = BitWise.byteArrayToLongLE(buffer);
            dibheader.setHres(longtemp);
            //System.out.println("resolucion horizontal: " + longtemp);

            //se lee y crea el campo resolucion vertical
            buffer = new byte[4];
            readed = bufferedinput.read(buffer);
            longtemp = BitWise.byteArrayToLongLE(buffer);
            dibheader.setVres(longtemp);
            //System.out.println("resolucion vertical: " + longtemp);

            //se lee y crea el campo numero de colores en la paleta
            buffer = new byte[4];
            readed = bufferedinput.read(buffer);
            longtemp = BitWise.byteArrayToLongLE(buffer);
            dibheader.setNcolors(longtemp);
            //System.out.println("numero de colores en la paleta: " + longtemp);

            //se lee y crea el campo colores importantes
            buffer = new byte[4];
            readed = bufferedinput.read(buffer);
            longtemp = BitWise.byteArrayToLongLE(buffer);
            dibheader.setNimpcolors(longtemp);
            //System.out.println("colores importantes: " + longtemp);
            //System.out.println("\n-------------------------------\n");
        }
    }
    //crea el arreglo de pixeles, conforme sea la informacion almacenada en DIBHeader
    //(se usan los campos bitspp,). Ademas se usa BitmapFileHeader, para capturar el offset del
    //pixelarray

    private void createPixelArray(DIBHeader dibheader) throws IOException {
        //System.out.println("vars: " + dibheader.getBitspp() + " " + dibheader.getWidth());
        //se calculan el tamaño de la fila y el del arreglo de pixeles
        long rowsize = (long) Math.ceil(dibheader.getBitspp() * dibheader.getWidth() * 0.125);
        //System.out.println("Pixelarraysize: " + pixelarraysize);
        int tambytespp = (dibheader.getBitspp()) / 8;
        //System.out.println("tambytespp: " + tambytespp);

        //se crea el pixel array conforme sea 
        //paleta de 24 bits (3 bytes RGB)
        createRGBPixelArray(dibheader);
        //System.out.println("\n-------------------------------\n");
    }

    private void createRGBPixelArray(DIBHeader dibheader) throws IOException {
        buffer = new byte[3];
        int read;
        int height = (int) dibheader.getHeight();
        //tamaño del acolchado (bites en cero que siguen despues de una fila)
        int paddingsize;
        int rowsize = (int) Math.floor((dibheader.getBitspp() * dibheader.getWidth()) * 0.125);
        //numero de pixeles por fila
        System.out.println("rowsize " + rowsize);
        System.out.println("bites size+padding: " + dibheader.getBmp_bytesz());
        System.out.println("width " + dibheader.getWidth());
        pixelarray = new RGB[(int) (dibheader.getWidth() * (height))];
        paddingsize = (int) ((dibheader.getWidth()*4) % rowsize);
        //si el ancho coincide con el numero de pixeles, hay que añadir el padding+=1
        if(paddingsize==dibheader.getWidth()){
            paddingsize=4;
        }
        System.out.println("paddingsize " + (int) (dibheader.getWidth() % rowsize));

        int contretorno = 0;
        //System.out.println("pixelarraysize*3 " + rowsize * height);
        while (contretorno < dibheader.getWidth() * height) {
            int j = 0;
            while (j < dibheader.getWidth()) {
                //System.out.println("contretorno= " + contretorno);
                //System.out.println("Posicion leida= " + contretorno);
                buffer = new byte[3];
                read = bufferedinput.read(buffer);
                pixelarray[contretorno] = new RGB(BitWise.byteArrayToIntLE(buffer));
                j++;
                contretorno++;
            }
            //System.out.println("----------------------------------------------- ");
            if (paddingsize != 0) {
                buffer = new byte[paddingsize];
                read = bufferedinput.read(buffer);
            }
        }
        /*
        contretorno = 0;
        while (contretorno < numpixelsperrow * height) {
        int j = 0;
        while (j < numpixelsperrow) {
        System.out.println("Red:" + pixelarray[contretorno].getRed());
        System.out.println("Green:" + pixelarray[contretorno].getGreen());
        System.out.println("Blue:" + pixelarray[contretorno].getBlue());
        j++;
        contretorno++;
        }
        }
         */
    }
}
